package link.correction;


/**
 * This class takes an array of bytes, and adds in an (8,4) Hamming code
 * and then returns an array of bytes which include those correction code
 * and the orginal data.
 * Note that a (8,4) encoding is not very optimal in terms of overhead
 * since 4 bits are encoded in 8 bits.
 * However this encoding provides 1 bit error correction and 2 bits error detection
 * @author Pim
 *
 */
public class Hamming8_4Adder implements CorrectionAdder {

	@Override
	public byte[] addCorrection(byte[] input) {
		byte[] res = new byte[input.length * 2];
		
		for(int i=0; i<input.length; i++){
			byte[] nibbles = toNibbles(input[i]);
			res[i*2] = calcHamming(nibbles[0]);
			res[i*2 + 1] = calcHamming(nibbles[1]);
		}
		
		return res;
	}
	
	/**
	 * returns the two nibbles in the provided byte, result[0] contains the
	 * rightmost 4 bits of b, result[1] contains the leftmost 4bits of b
	 * @param b the byte to split to nibbles
	 * @return an array containing the two nibbles
	 * @ensures \result.length == 2
	 */
	private static byte[] toNibbles(byte b){
		byte[] res = {0x00, 0x00};
		res[0] |= (b & 0x0F);
		res[1] |= (b >> 4) & 0x0F;
		return res;
	}
	
	/**
	 * calculates the (8,4) Hamming code for this nibble and returns the full byte
	 * containing the code
	 * @param b the nibble to calculate the hamming code of. The meaningfull bits are expected to be in 
	 * 			the rightmost four bits.
	 * @return a byte containing the hammingcode word for the provided nibble
	 */
	private static byte calcHamming(byte b){
		// b = - - - - d0 d1 d2 d3
		byte p0 = (byte) ((((b >> 3) & 0x01) + ((b >> 2) & 0x01) + (b & 0x01)) & 0x01); //p0 = d0 + d1 + d3
		byte p1 = (byte) ((((b >> 3) & 0x01) + ((b >> 1) & 0x01) + (b & 0x01)) & 0x01); //p1 = d0 + d2 + d3
		byte p2 = (byte) ((((b >> 2) & 0x01) + ((b >> 1) & 0x01) + (b & 0x01)) & 0x01); //p2 = d1 + d2 + d3
		byte p3 = (byte) ((((b >> 3) & 0x01) + ((b >> 2) & 0x01) + ((b >> 1) & 0x01) + (b & 0x01)
					+ p0 + p1 + p2) & 0x01); //p3 = p0 + p1 + p2 + d0 + d1 + d2 + d3
		//res = p0 p1 d0 p2 d1 d2 d3 p3
		byte res = p3; //add p3
		res |= ((b << 1) & 0x0E); //add in d1, d2 and d3
		res |= p2 << 4; //add in p2
		res |= ((b << 2) & 0x20); //add in d0
		res |= p1 << 6; // add in p1
		res |= p0 << 7;	// add in p0
		return res;
	}

}
